/*
 * IDE TIMER
 */
#include "../../include/const.h"
#include "../../include/timer.h" 
#include "../../include/task.h"  // for ticks and HZ
#include "../../include/monitor.h"   // for monitor_write
#include "../../include/console.h"
#include "../../include/keyboard.h" // for getchar()
#include "../../include/idelow.h" // CurrentChannel, InPortAta, HDC_STAT_BSY, HDC_ATAPIDELAY
#include "../../include/ide.h" //CC_DEVTYPE_ATAPI
#include "../../include/idetimer.h"

extern u32int ticks;
extern IdeChannel_Struct *CurrentChannel;

void GoIdle(){
  // int count;
  // while(count <= 1000000){
    // count++;
  // }
	u32int IF;
  __asm__ __volatile__ (
    "pushfl                 \n"
    "popl   %%eax           \n"
    "shrl   $9, %%eax       \n"
    "andl   $1, %%eax       \n" : "=a" (IF) : );
	__asm__ __volatile__("" : : : "eax");
	__asm__ ("sti" : : : "memory");
	__asm__ __volatile__ ("hlt");
	__asm__ __volatile__ (
		"pushfl                 \n"
		"popl   %%eax           \n"
		"orl    $(1 << 9), %%eax\n"
		"andl   %0, %%eax       \n"
		"pushl  %%eax           \n"
		"popfl                  \n" : : "d" ((IF & 0x01) << 9) );
	__asm__ __volatile__("" : : : "eax", "edx");

}
// ************************ Delay ***************************
// Start a timer
// **********************************************************
void Delay(u32int ms){
  u32int Start=ticks;
  // we work with integer and not float, we have resolution greater than 1 ms
  // so we must trunc at the tick higher expected
  u32int Stop;
  u8int Cross;
  if((ms*HZ)%1000 == 0){
    // We can wait exactely ms 
    Stop=Start+ms*HZ/1000;
  }
  else{
    // We must wait little more than ms because our 
    // timer resolution can't permit to wait exactely ms
    Stop=(Start+ms*HZ/1000)+1;
  }

  Cross=(Stop<Start);
  // need to pay attention while waiting 
  // for overflow of ticks and overflow of Stop
  while(Cross ? (ticks >= Start) || (ticks < Stop) : (ticks>=Start) && (ticks<Stop)){
    GoIdle();
  }
}

// ********************** TimerStart ************************
// Start a timer (filling a timer struct)
// **********************************************************
Timer_Struct TimerStart(u32int ms){       
  Timer_Struct Timer;
  Timer.Start=ticks;

  if(ms*HZ % 1000 == 0 ){
    // We can wait exactely ms
    Timer.Stop=Timer.Start+ms*HZ/1000;
  }
  else{
    // We must wait little more than ms because our
    // timer resolution can't permit to wait exactely ms
    Timer.Stop=(Timer.Start+ms*HZ/1000)+1;
  }
  Timer.Cross=(Timer.Stop<Timer.Start);
  return Timer;
}

// ********************* TimerElapsed ***********************
// Check if timer is elapsed
// **********************************************************
int TimerElapsed(Timer_Struct Timer){
  // need to pay attention for overflow of ticks while waiting
  // and overflow of Stop
  return (Timer.Cross ? (ticks < Timer.Start) && (ticks >= Timer.Stop) : (ticks<Timer.Start) || (ticks>=Timer.Stop));
}


// ********************** WaitForInt ************************
// Wait for interrupt
// The Timer MUST be started previously or this code will
// wait an undefined period of time
// **********************************************************
int WaitForInt(Timer_Struct Timer){
  while(TRUE){
    if(CurrentChannel->IntDone == TRUE){
      // reset interrupt flag;
      CurrentChannel->IntDone=FALSE;
      return FALSE;
    }
    if(TimerElapsed(Timer)){
      return TRUE;
    }
    GoIdle();
  }
}

// ********************** WaitForBsy ************************
// Wait for BSY = 0
// The Timer MUST be started previously or this code will
// wait an undefined period of time
// **********************************************************
int WaitForBsy(Timer_Struct Timer, u16int Port){
  u8int Status=0;

  while(TRUE){
    Status = InPortAta( Port );       // poll for not busy
    if(( Status & HDC_STAT_BSY ) == 0){
      return FALSE;
    }
    if(TimerElapsed(Timer)){               // time out yet ?
      return TRUE;
    }
    GoIdle();
  }
}

// ********************** AtapiDelay ************************
// Execute a delay only for atapi device
// **********************************************************
void AtapiDelay(int Dev){
  if(CurrentChannel->Device[Dev].Type == CC_DEVTYPE_ATAPI){
    Delay(HDC_ATAPIDELAY);
  }
}

// ********************** WaitKeyPress **********************
// Wait user press a key
// is for shell ide command and visualization
// **********************************************************
void WaitKeyPress(char *Message){
  int Tmp;
  if(Message){
    monitor_write("\n\r");
    monitor_write(Message);
    monitor_write("\n\r");
  }
  Tmp=getchar(); 
}

